package mop.domain.resource;

import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;

import mop.domain.task.Task;
import mop.domain.task.TaskPool;
import mop.exception.*;

/**
 * Class for ResourcePool.
 * This class is a singleton!
 * 
 * @invar 	No two resources have the same description.
 * 			|foreach(description) 
 *  		| isUniqueResource(description)
 *
 * @author	MOP Team 7
 */
public class ResourcePool 
{

	/**
	 * Creates a ResourcePool with no extra settings.
	 * Constructor must be at protected level.
	 * Allows inheritance, but no out of package creation.
	 * 
	 * @post resources is set to a new LinkedList
	 */
	protected ResourcePool()
	{
		this.resources = new LinkedList<Resource>();
	}
	
	
	/***************************************************************************
	 * 							instance
	 ***************************************************************************/
	
	/**
	 * Always use this function to access ResourcePool.
	 * 
	 * @return 	The static instance of ResourcePool.
	 */
	public static ResourcePool getInstance()
	{
		if(instance == null) 
		{
	         instance = new ResourcePool();
	    }
		
		return instance;	
	}
	
	/**
	 * instance to self
	 */
	private static ResourcePool instance = null;
	
	
	/***************************************************************************
	 * 					  Tasks requiring this resource
	 ***************************************************************************/
	
	/**
	 * Returns a list of tasks requiring this resource.
	 * 
	 * @return A list of tasks requiring this resource.
	 */
/*	public List<Task> getTasksRequiring(String resource)
	{
		Resource r = this.getResource(resource);
		if(r!=null)
			return r.getTasksRequiring();
		else
			return null;
	}*/
	
	/***************************************************************************
	 * 							resources container
	 ***************************************************************************/
	
	/**
	 * Returns a list of all the resources in resources.
	 * 
	 * @return	A list of Resource descriptions.
	 */
	public List<String> getResourceList()
	{
		List<String> result = new LinkedList<String>();
		
		for(Resource p : resources)
		{
			result.add(p.getDescription());
		}
		
		return result;
	}
	
	/**
	 * Returns a Resource object, based on a resource description.
	 * 
	 * @param 	description
	 * 			The resource description.
	 * @return	If the resource exists, a reference to the object is returned.
	 * 			Otherwise null.
	 */
	public Resource getResource(String description)
	{
		if(resources !=  null)
		{
			for(Resource r : resources)
			{
				if(r.getDescription().equals(description))
					return r;
			}
		}
		
		return null;
	}
	
	/**
	 * Returns a list of Resource objects, based on a list of resource descriptions.
	 * 
	 * @param 	resDescriptions
	 * 			The list of resource descriptions.
	 * @return	A list of corresponding Resources.
	 * 			If resDescriptions is null, an empty list of resources is returned.
	 */
	public List<Resource> getResourceList(List<String> resDescriptions)
	{
		List<Resource> result = new LinkedList<Resource>();
		
		if(resDescriptions != null)
		{
			for(String res : resDescriptions)
			{
				Resource r = getResource(res);
				if(r != null)
					result.add(r);
			}
		}
		
		return result;
	}
	
	/**
	 * Removes a resource from the list.
	 * 
	 * @param 	description
	 *  		The description of the resource you want to remove from the pool. 
	 * 
	 * @pre 	String exists in the resource database. If it doesn't, we ignore the remove.
	 * 
	 * @post 	The resource with his description = $description is removed from the list.
	 * 
	 * @throws 	IllegalTerminationException
	 * 			if the resource is required by a task.
	 */
	public void removeResource(String description) 
		throws IllegalTerminationException
	{
		Resource r = getResource(description);
		if(r != null)
		{
			if(r.getTasksRequiring().size() > 0)
				throw new IllegalTerminationException(description, "Resource");
			else
				resources.remove(r);
		}
	}
	
	 /**
	  * Checks if the given description is not already associated 
	  * with a resource in the container.
	  * 
	  * @param 	description
	  *  		The description to check.
	  * 
	  * @return true if description cannot be found in the database
	  */
	 public boolean isUniqueResource(String description)
	 {
		 for(Resource p: resources)
		 {
			 if(p.getDescription().equals(description))
				 return false;
		 }
		 
		 return true;
	 }
	
	/**
	 * Adds a tool to the resource pool.
	 * 
	 * @param 	resource
	 * 			The description of the tool to create.
	 * 
	 * @post	A new tool instance is created.
	 * 
	 * @throws 	IllegalDuplicateException
	 * 			if the resource is a duplicate.
	 * 			|!isUniqueresource(name)
	 * 
	 * from the class resource it throws  :       
	 * @throws 	IllegalDescriptionException
	 * 			if the description isn't a non-empty string.
	 * 			|!isValidDescription(description)
	 */
	public void addTool(String description) 
		throws IllegalDescriptionException, IllegalDuplicationException
	{
		if(isUniqueResource(description))
		{
			Tool p = new Tool(description);
			this.resources.add(p);
		}
		else
		{
			throw new IllegalDuplicationException(description);
		}
	}
	 
	/**
	 * Adds a room to the resource pool.
	 * 
	 * @param 	description
	 *  		The description of the room to create.
	 * 
	 * @post 	A new room instance is created.
	 * 
	 * @throws 	IllegalDuplicateException
	 * 			if the resource is a duplicate.
	 * 			|!isUniqueresource(name)
	 * 
	 * from the class resource it throws  :
	 * @throws 	IllegalDescriptionException
	 * 			if the description isn't a non-empty string.
	 * 			|!isValidDescription(description)
	 */
	public void addRoom(String description) 
		throws IllegalDescriptionException, IllegalDuplicationException
	{
		if(isUniqueResource(description))
		{
			Room p = new Room(description);
			this.resources.add(p);
		}
		else
		{
			throw new IllegalDuplicationException(description);
		}
	}
	
	
	/**
	 * list to store the projects in 
	 */
	private List<Resource> resources;
	
	
	/***************************************************************************
	 * 							   reservation
	 ***************************************************************************/
	
	/**
	 * Adds a reservation for a resource.
	 * 
	 * @param	resource
	 * 			The resource description as a String.
	 * @param 	start
	 * 			The start time as a Timestamp.
	 * @param	end
	 * 			The end time as a Timestamp.
	 * 
	 * @post	A reservation for the resource is made.
	 * 
	 * @throws	IllegalReservationException
	 * 			If the start time is greater than the end time.
	 * @throws	UnavailableResourceException 
	 * 			If the resource is unavailable at that time.
	 * @throws	IllegalResourceException
	 * 			If the resource does not exist.
	 */
	public void addReservation(String resource, Timestamp start, Timestamp end) 
		throws IllegalResourceException, IllegalReservationException, UnavailableResourceException
	{
		Resource r = this.getResource(resource);
		if(r != null)
		{
			r.addReservation(start, end);
		}
		else
		{
			throw new IllegalResourceException(resource);
		}
	}
	
	/**
	 * Returns a list of strings representing the reservations for a given resource.
	 * 
	 * @param 	resource
	 * 			The description of a resource.
	 * 
	 * @return	A list of reservation strings.
	 * 
	 * @throws	IllegalResourceException 
	 * 			If the Resource cannot be found.
	 */
	public List<String> getReservations(String resource) 
		throws IllegalResourceException
	{
		LinkedList<String> result = new LinkedList<String>();
		Resource res = this.getResource(resource);
		
		if(res != null)
		{
			for(Reservation r : res.getReservations())
			{
				result.add(r.toString());
			}
		}
		else
		{
			throw new IllegalResourceException(resource);
		}
		
		return result;
	}
	
}